#!/usr/bin/python3
# -*- coding: UTF-8 -*- 
# 设置utf-8  显示中文
"""
@Author: guozg
@File：handle_dependence_data.py
"""

import sys
from string import Template
import jsonpath
import pytest
import requests

sys.path.append("../")
from common.yml.get_yml_keys import GetYmlKeys
from config.get_conf_data import GetConfData
from common.util.handle_jsonfile import HandleJsonFile
from login.login import Login
from common.yml.handle_dependence.get_all_dependence_case import GetAllDepCase
from common.yml.handle_dependence.get_yml_func_data import GetFuncData
from common.yml.handle_checkdata import HandleCheck
from common.yml.handle_response import HandleResponse


class HandleDependenceData:
    """
    处理case里的所有依赖数据，包括多层依赖以及嵌套依赖的数据.
    其中多层依赖,仅限制于request与response.当为json为sql时,不支持.
    """

    def __init__(self, yml_data: dict):
        """
        处理数据依赖,将根据文件的类型,而进行不同的操作.  \n
        yml文件:对应的是依赖类型为 request 或 response
        json文件: 对应的是依赖类型为: json
        sql:
        :param yml_data: 处理后的yml数据.(page层与case层已合并,且默认值已处理完成)
        """

        self.__conf = GetConfData()
        self.__ymlkeys = GetYmlKeys()
        self.__login = Login()
        self.__yml_data = yml_data
        self.__depcase = GetAllDepCase()
        self.__funcdata = GetFuncData()
        self.__hjson = HandleJsonFile()
        self.__hresponse = HandleResponse()
        self.__hcheck = HandleCheck()

    def run_all_dependence_case(self)->dict:
        """
        运行所有的依赖接口(注意在运行之前,会先进行反转,然后再按顺序去执行)  \n
        当运行到最后一个接口后,会将当前的case的 依赖数据 等,进行相关的替换操作 \n
        即:最后会将当前的case对应的ymldata 依赖的值替换后,再回传回去. \n
        :return: dict
        """
        # 获取所有的依赖数据,并进行反转.方便进行
        rely_list = self.__depcase.get_all_case(self.__yml_data)[::-1]
        print(f"依赖的接口数据，进行反转后如下：\n{rely_list}")
        count = len(rely_list)
        # 存储响应结果
        res = None
        # 存储上一次的requestdata(依赖数据里有request依赖)
        last_request = None
        # 保存token,无token 无法执行
        token = None
        # 登录类型 app 还是web
        login_type = None

        # **************** 需要用到的一些key ********************
        api_data_key = self.__ymlkeys.get_api_data_key()
        data_type_key = self.__ymlkeys.get_data_type_key()
        req_data_key = self.__ymlkeys.get_request_data_key()

        dep_login_key = self.__ymlkeys.get_dependence_login_key()
        login_data_key = self.__ymlkeys.get_login_data_key()
        login_type_key = self.__ymlkeys.get_login_type_key()
        token_key = self.__ymlkeys.get_token_filepath_key()

        app_str = self.__conf.get_app_str()
        web_str = self.__conf.get_web_str()

        request_str = self.__conf.get_request_str()
        response_str = self.__conf.get_response_str()

        # dependence_case key
        dep_case_key = self.__ymlkeys.get_dependence_case_key()
        # dependence_case_data key
        dep_casedata_key = self.__ymlkeys.get_dependence_case_data_key()
        # dependence_type key
        dep_type_key = self.__ymlkeys.get_dependence_type_key()
        # dependence_key  key
        dep_key = self.__ymlkeys.get_dependence_key()
        # replace_key key
        rep_key = self.__ymlkeys.get_replace_key()
        # jsonfilepath key
        jsonfilepath_key = self.__ymlkeys.get_jsonfilepath_key()


        case_join_page_flag = self.__conf.get_case_join_page()

        # 返回的结果中的key
        caseid_key = self.__conf.get_caseid()
        filetype_key = self.__conf.get_filetype()
        filedata_key = self.__conf.get_filedata()
        testinit_key = self.__conf.get_testinit()
        json_str = self.__conf.get_json_str()
        sql_str = self.__conf.get_sql_str()

        if count > 0:
            for item in rely_list:
                # 要先获取caseid
                caseid = item[caseid_key]
                '''
                此时要判断 item的 caseid 和类型的.当caseid = None时,此时要判断filetype的类型是json 还是 sql.
                目前当为sql时,未做处理,后续的逻辑再补充.
                当caseid 不为None时,表示依赖的类型为request 或 response
                '''
                if caseid == None:
                    # 获取filetype的值
                    filetype = item[filetype_key]
                    if filetype == json_str:
                        filedata = item[filedata_key]
                        # 对res 直接进行赋值
                        res = filedata

                    # 表示为sql,暂时不做处理.
                    else :
                        pytest.fail(f"当依赖类型为sql时,这块的逻辑还没有完善,暂时不支持.")

                    """
                    1:由于依赖的数据里,可能存在着登录依赖,但是登录的yml数据与其他的数据有一定的差异,所以要优先判断是否有登录
                    2:要检测是否有登录,目前只需要检测caseid里是否有None字符串即可. demo/test_data.yml::test_init:::None
                    """

                #demo/test_data.yml::test_init:::None
                elif "None" in caseid:
                    # 处理登录
                    casestr = caseid.split(case_join_page_flag)[0]
                    logindata = self.__funcdata.get_yml_func_data(casestr)
                    login_type = logindata[login_type_key]
                    # 获取登录请求参数
                    login_data = logindata[login_data_key]
                    if login_type == app_str:
                        res = self.__login.get_app_login_response(login_data)
                    # 表示是web api登录
                    else:
                        res = self.__login.get_web_login_response(login_data)
                    # 存储登录的请求参数
                    last_request = login_data

                # 表示为正常的 request 或response 依赖.
                else:
                    # 获取 依赖的接口的 ymldata数据
                    ymldata = item[filedata_key]
                    api_data = ymldata[api_data_key]
                    # 获取data 的类型,是data,还是json,或者params--暂时用不着
                    data_type = api_data[data_type_key]
                    # 获取请求参数 值
                    request_data = api_data[req_data_key]
                    # 是否要登录
                    whether_login = api_data[dep_login_key]
                    # 是否有数据依赖
                    whether_dependence = api_data[dep_case_key]

                    # 表示需要登录获取token
                    if whether_login == True:
                        logindata = api_data[login_data_key]
                        # 登录类型,app还是web
                        login_type = logindata[login_type_key]
                        # 登录的参数值
                        login_data_second = logindata[login_data_key]
                        # 获取token_path的值
                        token_path = logindata[token_key]

                        """
                        不在判断是全局登录依赖,还是local登录依赖.直接判断 token_path的值,当有值时,直接取,否则就使用login_data
                        里的值进行登录
                        """
                        from common.util.get_file_path import GetFilePath
                        fpath = GetFilePath()
                        flag = fpath.check_tokenfile_available(login_type,token_path)
                        # 表示json文件存在,且有效.
                        if flag == True:
                            token = self.__hjson.read_json_file_to_dict(token_path)

                        # 表示json文件无效.
                        else:
                            # 判断是哪种类型的登录
                            if login_type == app_str:
                                token = self.__login.get_app_token(login_data_second)

                            #表示为web登录
                            else:
                                token = self.__login.get_web_token(login_data_second)

                        if login_type == app_str:
                            token = {"headers": token}

                        # 表示为web登录
                        else:
                            token = {"cookies": token}

                    # # 表示不需要登录
                    # else:
                    """
                    注意:当将依赖的case 反转后,只有执行第一个依赖的case时,不存在数据依赖,其余的case都存在数据依赖.
                    1:判断是否有数据依赖,如果有,则还要进行参数值的替换
                    2:当有数据依赖时,表示res(用于接收上个依赖接口请求响应的结果)有值
                    3:当有接口依赖时,还要区分是request请求参数的值的依赖 还是 response 值的依赖
                    4:为了以防万一,当为response依赖时,可以对res做判断,当为None时,直接将该case置为fail.
                    """
                    if whether_dependence == True:
                        # 获取依赖数据
                        dep_data = api_data[dep_casedata_key]
                        # 获取依赖类型
                        dep_type = dep_data[dep_type_key]

                        # 要判断依赖类型是request还是response
                        if dep_type == request_str:
                            # 此时进行last_request是否为None的判断,当为None时,要将case置为fail
                            if last_request == None:
                                pytest.fail(f"在执行被依赖的case:{caseid}时,依赖的上一次的接口请求参数:None,故无法执行此被依赖的case"
                                            f"所以要将当前的case(非被依赖的case)置为fail")

                            # 表示此时要从依赖的接口的请求参数里取值
                            else:
                                # 获取被依赖的key 和 要替换的key
                                dep_key_list = dep_data[dep_key]
                                replace_key_list = dep_data[rep_key]
                                # 进行到这一步,说明dependence_key 与 replace_key的个数是一致的.所以在此不需要再校验两者的个数是否相同
                                key_list_count = len(dep_key_list)
                                for i in range(key_list_count):
                                    # 获取依赖的key
                                    dependence_key = dep_key_list[i]
                                    # 获取要替换的key
                                    replace_key = replace_key_list[i]
                                    # 从request请求参数里获取被依赖的结果
                                    dependence_value = jsonpath.jsonpath(last_request, dependence_key)[0]
                                    # # 使用模板
                                    # request_data_tmp = Template(json.dumps(request_data))
                                    # # 重新拼接request_data的值
                                    # request_data = json.loads(
                                    #     request_data_tmp.substitute(**{replace_key: dependence_value}))

                                    """
                                    不在使用模板替换技术,因为字段里存在.的情况,如 actionType.id 当出现这种情况时,使用模板替换,会报错
                                    所以要注意上面几行的代码
                                    """
                                    # 替换请求数据.
                                    request_data[data_type][replace_key] = dependence_value

                                # 执行被依赖的接口.当需要登录时,表示要传入token
                                if whether_login == True:
                                    res = requests.request(**request_data, **token)
                                # 表示不需要传入token
                                else:
                                    res = requests.request(**request_data)
                                last_request = request_data


                        # 表示是 response 数据依赖
                        elif dep_type == response_str:
                            # 此时要进行res是否为None的判断,当为None时,要将case置为fail
                            if res == None:
                                pytest.fail(f"在执行被依赖的case:{caseid}时,上一次的接口响应结果为None,故无法执行此被依赖的case"
                                            f"所以要将当前的case(非被依赖的case)置为fail")
                            # 此时要从依赖的结果里进行获取相应的字段值,来替换request请求的字段值
                            else:
                                # 获取被依赖的key 和 要替换的key
                                dep_key_list = dep_data[dep_key]
                                replace_key_list = dep_data[rep_key]
                                # 进行到这一步,说明dependence_key 与 replace_key的个数是一致的.所以在此不需要再校验两者的个数是否相同
                                key_list_count = len(dep_key_list)

                                for i in range(key_list_count):
                                    # 获取依赖的key
                                    dependence_key = dep_key_list[i]
                                    # 获取要替换的key
                                    replace_key = replace_key_list[i]

                                    # 获取被依赖的结果
                                    # 加一层判断,当直接从json文件里获取数据时,是没有json()方法的.
                                    if isinstance(res,dict):
                                        dependence_value = jsonpath.jsonpath(res, dependence_key)[0]
                                    else:
                                        dependence_value = jsonpath.jsonpath(res.json(), dependence_key)[0]
                                    # # 使用模板
                                    # request_data_tmp = Template(json.dumps(request_data))
                                    # # 重新拼接request_data的值
                                    # request_data = json.loads(
                                    #     request_data_tmp.substitute(**{replace_key: dependence_value}))

                                    """
                                    不在使用模板替换技术,因为字段里存在.的情况,如 actionType.id 当出现这种情况时,使用模板替换,会报错
                                    所以要注意上面几行的代码
                                    """
                                    request_data[data_type][replace_key] = dependence_value

                                # 执行被依赖的接口.当需要登录时,表示要传入token
                                if whether_login == True:
                                    res = requests.request(**request_data, **token)
                                # 表示不需要登录,即不需要传入token
                                else:
                                    res = requests.request(**request_data)
                                last_request = request_data


                        # 表示为 sql 数据依赖,该逻辑暂时未实现
                        elif dep_type == sql_str:
                            pass
                        # 表示为json 数据依赖
                        else:
                            # 获取被依赖的key 和 要替换的key
                            dep_key_list = dep_data[dep_key]
                            replace_key_list = dep_data[rep_key]
                            # 进行到这一步,说明dependence_key 与 replace_key的个数是一致的.所以在此不需要再校验两者的个数是否相同
                            key_list_count = len(dep_key_list)
                            # 获取jsonfilepath的值
                            jsonfilepath = dep_data[jsonfilepath_key]
                            # 获取依赖的json数据
                            res = self.__hjson.read_json_file_to_dict(jsonfilepath)

                            for i in range(key_list_count):
                                # 获取依赖的key
                                dependence_key = dep_key_list[i]
                                # 获取要替换的key
                                replace_key = replace_key_list[i]

                                # 获取被依赖的结果
                                # 加一层判断,当直接从json文件里获取数据时,是没有json()方法的.
                                if isinstance(res, dict):
                                    dependence_value = jsonpath.jsonpath(res, dependence_key)[0]
                                else:
                                    dependence_value = jsonpath.jsonpath(res.json(), dependence_key)[0]
                                # # 使用模板
                                # request_data_tmp = Template(json.dumps(request_data))
                                # # 重新拼接request_data的值
                                # request_data = json.loads(
                                #     request_data_tmp.substitute(**{replace_key: dependence_value}))

                                """
                                不在使用模板替换技术,因为字段里存在.的情况,如 actionType.id 当出现这种情况时,使用模板替换,会报错
                                所以要注意上面几行的代码
                                """
                                request_data[data_type][replace_key] = dependence_value

                            # 执行被依赖的接口.当需要登录时,表示要传入token
                            if whether_login == True:
                                res = requests.request(**request_data, **token)
                            # 表示不需要登录,即不需要传入token
                            else:
                                res = requests.request(**request_data)
                            last_request = request_data



                    # 表示没有数据依赖
                    else:
                        # 当需要登录时, 表示要传入token
                        if whether_login == True:
                            res = requests.request(**request_data, **token)

                        else:
                            # 存储响应结果
                            res = requests.request(**request_data)
                        # 在实际的执行接口的过程中，有时会出现无响应或反应的结果为None的情况
                        text = res.text
                        if text == None or text == "":
                            res = None

                        # 存储请求参数
                        last_request = request_data

                    """检测是否保存响应结果"""
                    self.__hresponse.handle_response(res, ymldata)

                    """数据检查,同时按配置保存校验结果"""
                    self.__hcheck.handle_checkdata(res, ymldata)


        # # 在返回结果时,要先判断所依赖的数据类型是request,还是response
        # if self.__yml_data[self.api_data_key][self.dependence_type_key] == self.request_str:
        #     return last_request
        # else:
        #     return res.json()

        # 在返回结果时,要先判断所依赖的数据类型是request,还是response,然后将传入的ymldata数据直接进行替换
        dependence_data = self.__yml_data[api_data_key][dep_casedata_key]
        dependence_type = dependence_data[dep_type_key]

        dependence_key_list = dependence_data[dep_key]
        replace_key_list = dependence_data[rep_key]
        key_list_count = len(dependence_key_list)
        # 获取传入的ymldata里的requestdata
        requestdata = self.__yml_data[api_data_key][req_data_key]
        data_type = self.__yml_data[api_data_key][data_type_key]
        for i in range(key_list_count):
            # 获取依赖的key
            dependence_key = dependence_key_list[i]
            # 获取要替换的key
            replace_key = replace_key_list[i]
            if dependence_type == request_str:
                # 从request请求参数里获取被依赖的结果
                dependence_value = jsonpath.jsonpath(last_request, dependence_key)[0]
            else:

                # 先判断res 是否为None
                if res == None:
                    pytest.fail(f"在执行被依赖的接口或获取被依赖的数据时，响应结果为None，故将case置为fail")
                else:
                    # 表示是直接从json文件里获取的响应结果
                    if isinstance(res,dict):
                        dependence_value = jsonpath.jsonpath(res, dependence_key)[0]
                    else:
                        # 获取被依赖的结果
                        dependence_value = jsonpath.jsonpath(res.json(), dependence_key)[0]
            """
            由于依赖的字段里，也存在带.的情况，如 actionType.id。当出现这种情况时，就不能使用模板替换技术，
            此时进行进行赋值即可。
            下面的两行代码，需要注释掉，直接进行赋值
            """
            # # 使用模板
            # request_data_tmp = Template(json.dumps(requestdata))
            # # 重新拼接request_data的值
            # requestdata = json.loads(request_data_tmp.substitute(**{replace_key: dependence_value}))

            requestdata[data_type][replace_key] = dependence_value

        # 再重对传入的ymldata里的request_data重新赋值
        self.__yml_data[api_data_key][self.__ymlkeys.get_request_data_key()] = requestdata
        # 返回拼接请求参数值后的ymldata
        return self.__yml_data


